import FCVector , FCMathUtil
import random , math

class Behavior:
    def __init__( self, _agent ):
        '''
        flag mode
        wallAvoidance = 1
        seek = 2
        wander = 4
        '''
        self.idx = _agent.mIdx
        self.agent = _agent
        
        self.flag = 0
        self.steering = FCVector.vector2D( 0, 0 )
        self.target = FCVector.vector2D( 0, 0 )

        self.wanderTarget = FCVector.vector2D( 0, 0 )
        self.wanderJitter = 500.0 * random.uniform( 0.5, 1.5 )
        self.wanderRadius = 200.0
        self.wanderDistance = 25.0

        self.ditectionLength = 30.0
        self.feelerLength = 60.0

        self.weightWallAvoidance = 1.0
        self.weightSeek = 1.0
        self.weightWander = 10.0
        
        _theta = random.uniform( -1, 1 ) * math.pi * 2.0
        self.wanderTarget.x = self.wanderRadius * math.cos( _theta )
        self.wanderTarget.y = self.wanderRadius * math.sin( _theta )

        self.feelers = [ FCVector.vector2D( 0, 0 ) for _cnt in xrange(3) ]

    def accumlateForce( self, _runningTot, _forceToAdd ):
        _magnitudeSoFar = _runningTot.length()
        _magnitudeRemaining = self.agent.maxForce - _magnitudeSoFar
        _magnitudeToAdd = _forceToAdd.length()

        if 0.0 >= _magnitudeRemaining:
            return False

        if _magnitudeToAdd < _magnitudeRemaining:
            _runningTot.x += _forceToAdd.x
            _runningTot.y += _forceToAdd.y
        else:
            _temp = FCVector.normalize( _forceToAdd ) * _magnitudeRemaining
            _runningTot.x += _temp.x
            _runningTot.y += _temp.y

        return True

    def calculate( self, _dt ):
        _force = FCVector.vector2D( 0, 0 )
        self.steering.x = 0
        self.steering.y = 0
        
        if self.isOn( 1 ):
            _force = self.wallAvoidance( self.agent.mSimulator.walls ) * self.weightWallAvoidance
            if not self.accumlateForce( self.steering, _force ):
                return self.steering

        if self.isOn( 2 ):
            _force = self.seek( self.target ) * self.weightSeek
            if not self.accumlateForce( self.steering, _force ):
                return self.steering
                
        if self.isOn( 4 ):
            _force = self.wander( _dt ) * self.weightWander
            if not self.accumlateForce( self.steering, _force ):
                return self.steering

        return self.steering

    def createFeelers( self ):
        self.feelers[0] = self.agent.position + ( self.feelerLength * self.agent.heading )
        
        _heading = self.agent.heading
        _temp = FCVector.vector2D( _heading.x, _heading.y )
        FCVector.rotate( _temp, math.pi / 2.0 * 3.5 )
        self.feelers[1] = self.agent.position + ( self.feelerLength / 2.0 * _temp )
        
        _temp = FCVector.vector2D( _heading.x, _heading.y )
        FCVector.rotate( _temp, math.pi / 2.0 * 0.5 )
        self.feelers[2] = self.agent.position + ( self.feelerLength / 2.0 * _temp )
        
    def seek( self, _target ):
        _desiredVelocity = _target - self.agent.position
        _desiredVelocity.truncate( self.agent.maxSpeed / 2.0 )

        return _desiredVelocity - self.agent.velocity

    def wander( self, _dt ):
        _jitterTime = self.wanderJitter * _dt
        
        self.wanderTarget.x = random.uniform( -1, 1 ) * _jitterTime
        self.wanderTarget.y = random.uniform( -1, 1 ) * _jitterTime
        self.wanderTarget *= self.wanderRadius
        
        _target = FCVector.vector2D( 0, 0 )
        _target.x = self.wanderTarget.x + self.wanderDistance
        _target.y = self.wanderTarget.y
        _target = FCMathUtil.pointToworldspace( _target, self.agent.heading, self.agent.side, self.agent.position )
        
        return _target - self.agent.position
        
    def wallAvoidance( self, _walls ):
        self.createFeelers()
        
        _distToThisIP = [0.0]
        _distToClosestIP = 99999999
        _closestWall = -1
        _steering = FCVector.vector2D( 0, 0 )
        _point = [ FCVector.vector2D( 0, 0 ) ]
        _closestPoint = FCVector.vector2D( 0, 0 )
        
        for _idx in xrange( 3 ):
            for _wall in xrange( len(_walls) ):
                if LineIntersection2D( self.agent.position, self.feelers[_idx], _walls[_wall].from_, _walls[_wall].to, _distToThisIP, _point ):
                    if _distToThisIP[0] < _distToClosestIP:
                        _distToClosestIP = _distToThisIP[0]
                        _closestWall = _wall
                        _closestPoint.x = _point[0].x
                        _closestPoint.y = _point[0].y
                        
            if 0 <= _closestWall:
                _overShoot = self.feelers[_idx] - _closestPoint
                _steering = _walls[_closestWall].normal * _overShoot.length()
                
        return _steering
        
    def isOn( self, _flag ):
        return (self.flag & _flag) == _flag
        
    def on( self, _flag ):
        if not self.isOn( _flag ): self.flag |= _flag
        
    def off( self, _flag ):
        if self.isOn( _flag ): self.flag ^= _flag